﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Flagwind.Common;

namespace Flagwind.Data
{
	public static class ConditionExtensions
	{
		public static Comparer And(this Comparer comparer1, Comparer comparer2)
		{
			return new Comparer(comparer1, CompareLogic.And, comparer2);
		}

		public static Comparer Or(this Comparer comparer1, Comparer comparer2)
		{
			return new Comparer(comparer1, CompareLogic.Or, comparer2);
		}

		public static Comparer Translate<E>(this ICondition condition, Command command, E entity) where E : EntityBase, new()
		{
			return Translate<E>(condition, command, new List<E> { entity });
		}

		public static Comparer Translate<E>(this ICondition condition, Command command, params E[] entities) where E : EntityBase
		{
			return Translate<E>(condition, command, entities.ToList());
		}

		public static Comparer Translate<E>(this ICondition condition, Command command, IEnumerable<E> entities) where E : EntityBase
		{
			if(condition == null)
				return null;

			var result = new Comparer(command);
			var conditions = condition as ConditionCollection;

			if(conditions != null)
			{
				if(conditions.Count == 0)
					return null;

				// 处理多个条件。
				for(var i = 0; i < conditions.Count; i++)
				{
					if(i == 0)
					{
						result = Translate(conditions[i], command, entities);
					}
					else
					{
						result = conditions.ConditionCombination == ConditionCombination.And ? result.And(Translate(conditions[i], command, entities)) : result.Or(Translate(conditions[i], command, entities));
					}
				}
			}
			else
			{
				// 处理单个条件。
				result = GetComparer(condition as Condition, command, entities);
			}

			return result;
		}

		public static Command4 OrderBy<E>(this Command1 command, ISorting sorting, E entitiy) where E : EntityBase
		{
			return OrderBy<E>(command, sorting, new List<E> { entitiy });
		}

		public static Command4 OrderBy<E>(this Command1 command, ISorting sorting, params E[] entities) where E : EntityBase
		{
			return OrderBy<E>(command, sorting, entities.ToList());
		}

		public static Command4 OrderBy<E>(this Command1 command, ISorting sorting, IEnumerable<E> entities) where E : EntityBase
		{
			if(sorting == null)
				return null;

			Command4 result = null;

			var sortings = sorting as SortingCollection;

			if(sortings != null)
			{
				if(sortings.Count == 0)
					return null;

				foreach(var item in sortings)
				{
					result = OrderBy(command, item, entities);
				}
			}
			else
			{
				var buffer = sorting as Sorting;

				// 查找包含条件的实体。
				var entity = entities.FirstOrDefault(p => p.GetPropertyNames().Contains(buffer.Name));

				if(entity == null)
					throw new ArgumentException(string.Format("排序名 '{0}' 未定义，请检查。", buffer.Name));

				result = command.OrderBy(entity[buffer.Name], buffer.Mode == SortingMode.Ascending ? "ASC" : "DESC");
			}

			return result;
		}

		private static Comparer GetComparer<E>(Condition condition, Command command, IEnumerable<E> entities) where E : EntityBase
		{
			if(string.IsNullOrWhiteSpace(condition.Name))
				throw new ArgumentNullException(nameof(condition.Name));

			// 查找包含条件的实体。
			var entity = entities.FirstOrDefault(p => p.GetPropertyNames().Contains(condition.Name));

			if(entity == null)
				throw new ArgumentException(string.Format("条件名 '{0}' 未定义，请检查。", condition.Name));

			var enttityType = entity.GetType();
			var properties = enttityType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
			var propertyInfo = properties.FirstOrDefault(p => p.Name.Equals(condition.Name));
			var propertyType = propertyInfo.PropertyType.IsNullable() ? Nullable.GetUnderlyingType(propertyInfo.PropertyType) : propertyInfo.PropertyType;

			var result = new Comparer(command);
			var typeCode = Type.GetTypeCode(propertyType);

			// 优先处理特殊操作符。
			switch(condition.Operator)
			{
				case ConditionOperator.IsNull:
					return result.IsNull(entity[condition.Name]);
				case ConditionOperator.IsNotNull:
					return result.IsNotNull(entity[condition.Name]);
				case ConditionOperator.In:
					return result.Compare(entity[condition.Name], CompareOperator.In, GetValues(condition));
				case ConditionOperator.NotIn:
					return result.Compare(entity[condition.Name], CompareOperator.NotIn, GetValues(condition));
			}

			// 特殊处理日期类型。
			if(typeCode == TypeCode.DateTime && condition.Operator == ConditionOperator.Equals)
			{
				var date = Flagwind.Common.Convert.ConvertValue<DateTime>(condition.Value);

				if(date.TimeOfDay == TimeSpan.Zero)
				{
					var startTime = new DateTime(date.Year, date.Month, date.Day, 0, 0, 0);
					var endTime = new DateTime(date.Year, date.Month, date.Day, 23, 59, 59);

					return result.Compare(entity[condition.Name], CompareOperator.GreaterThanOrEqual, startTime).And(result.Compare(entity[condition.Name], CompareOperator.LessThanOrEqual, endTime));
				}
			}

			// 调用通用转换类，转换一下值。
			var value = Flagwind.Common.Convert.ConvertValue(condition.Value, propertyType);

			// 解析操作符并返回比较器。
			return result.Compare(entity[condition.Name], GetOperator(condition.Operator), value);
		}

		private static CompareOperator GetOperator(ConditionOperator @operator)
		{
			switch(@operator)
			{
				case ConditionOperator.Equals:
					return CompareOperator.Equal;
				case ConditionOperator.NotEqual:
					return CompareOperator.NotEqual;
				case ConditionOperator.GreaterThan:
					return CompareOperator.Greater;
				case ConditionOperator.GreaterThanOrEqual:
					return CompareOperator.GreaterThanOrEqual;
				case ConditionOperator.LessThan:
					return CompareOperator.LessThan;
				case ConditionOperator.LessThanOrEqual:
					return CompareOperator.LessThanOrEqual;
				case ConditionOperator.Like:
					return CompareOperator.Like;
				case ConditionOperator.In:
					return CompareOperator.In;
				case ConditionOperator.NotIn:
					return CompareOperator.NotIn;
			}

			return CompareOperator.Equal;
		}

		private static object[] GetValues(Condition condition)
		{
			if(condition.Value == null)
				throw new ArgumentNullException(nameof(condition.Value));

			if(condition.Value is Array || condition.Value is IList)
			{
				var values = new List<object>();
				var collection = condition.Value as IEnumerable;

				foreach(var item in collection)
				{
					values.Add(item);
				}

				return values.ToArray();
			}

			return condition.Value.ToString().Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
		}
	}
}
